﻿#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <Windows.h>


void text()
{
#ifdef max_debug
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d y=%d z=%d\n\n", x, y, z);//输出的结果是什么？
#endif // max_debug
}


int main()
{
	//预定义符号
	//assert输出的警告信息的来源也是这里
	printf("current_file=%s\n", __FILE__);//显示当前的源文件地址和名字
	printf("current_line=%d\n", __LINE__);//显示当前行号
	printf("current_date=%s\n", __DATE__);//显示编译日期
	printf("current_time=%s\n\n\n\n",__TIME__);//显示编译时间

	//#define定义常量
#define MAX 1000
#define reg register //为 register这个关键字，创建⼀个简短的名字
	// 如果定义的 stuff过⻓，可以分成⼏⾏写，除了最后⼀⾏外，每⾏的后⾯都加⼀个反斜杠(续行符)。
#define DEBUG_PRINT printf("/*\nfile:%s\nline:%d\ndate:%s\ntime:%s\n*/\n" ,\
							strrchr(__FILE__, '\\')+1,__LINE__ , \
							__DATE__,__TIME__ )
	DEBUG_PRINT;//调用自定义的DEBUG宏函数//并使用strrchr查找\的位置实现只打印文件名，避免路径过长
	//宏定义后面建议不要加;在需要使用时看情况决定是否添加
//宏的申明⽅式
//#define name( parament-list ) stuff
	//参数列表的左括号必须与name紧邻，如果两者之间有任何空⽩存在，参数列表就会被解释为stuff的⼀部分
#define SQUARE( x ) x * x
	//上面声明的宏函数有问题例如
	printf("\n\n\n#define SQUARE( x ) x * x\n");
	printf("SQUARE(5+1)=%d\n", SQUARE(5+1));
#define DOUBLE(x) (x) + (x)
	printf("#define DOUBLE(x) (x) + (x)\n");
	printf("10*DOUBLE(5)=%d\n", 10 * DOUBLE(5));
	//因为是文本替换
	//  SQUARE(5+1)   == 5+1*5+1==5+5+1==11

	//这样做可以解决
#define SQUARE_2( x ) (x) * (x)
	printf("*correct*\n#define SQUARE_2( x ) (x) * (x)\n");
	printf("SQUARE_2(5 + 1)=%d\n", SQUARE_2(5 + 1));
#define DOUBLE_2(x) ((x) + (x))
	printf("#define DOUBLE_2(x) ((x) + (x))\n");
	printf("10*DOUBLE_2(5)=%d\n\n\n", 10 * DOUBLE_2(5));
	//所以⽤于对数值表达式进⾏求值的宏定义都应该⽤这种⽅式加上括号，避免在使⽤宏时由于参数中的
	// 操作符或邻近操作符之间不可预料的相互作⽤。
	// 
	// 
	// 
	//下面这个宏就能证明带有副作用的参数会有问题
#define max_debug//宏定义//和变量一样有作用域
//#undef max_debug//取消宏定义
#ifdef max_debug
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d y=%d z=%d\n\n", x, y, z);//输出的结果是什么？
#endif // max_debug
	#define max_debug//宏定义
	//所以尽量避免使用复杂的宏
	text();
	return 0;
}

//放置重复包含头文件有两种方法
//1使用条件编译
//2使用#pragma once